// 创建Observer类
class Observer {
  // 从Vue实例中创建Observer实例传递过来的data数据
  constructor (data) {
    // 存储
    this.data = data
    // 劫持this.data
    this.ergodicData(this.data)
  }
  // 遍历所有 data 数据，交给 kidnap 劫持
  ergodicData (data) {
    Object.keys(data).forEach(key => this.kidnap(key, data[key]))
  }
  // 调用 hijackingData 函数进行数据劫持
  kidnap (key, value) {
    // 最终劫持给 this.data
    hijackingData(this.data, key, value)
  }
}
// 这里跟称好多函数是为了让功能更加单一，增加复用性


// 数据劫持函数
// 参数1: Observer 实例的 data
// 参数2: 属性名
// 参数3:属性值
function hijackingData (data, key, value) {
  // 为每个data数据都要创建一个Dep实例
  const dep = new Dep()

  // 调用 foundObserver 进行对象检测
  foundObserver(value)
  // 如果检测通过 value 不是对象，进行数据劫持
  Object.defineProperty(data, key, {
    // 是否可遍历（默认false）
    enumerable: true,
    // 是否可再次设置（默认false）
    configurable: true,
    // get方法：直接返回数据
    get () {
      // 做出动作
      console.log('Observer读取数据了')
      // 在创建订阅者的时候必然会触发一次 get ，这时候订阅者会把自己设置给 Dep 类的静态属性中，
      // 我们可以通过查看Dep类这个属性是否有值来的判断当前get是不是 订阅者触发
      // 如果有代表当前 get 是订阅者用来获取初始值触发的，那么就把订阅者添加到dep实例的数组中
      if (Dep.watcher) dep.addSub(Dep.watcher)
      // 直接返回value
      return value
    },
    // set方法：修改数据
    set (newValue) {
      // 做出动作
      console.log('Observer修改数据了')
      // 如果新值和老值一样直接返回
      if (newValue === value) return
      // 修改数据
      value = newValue
      // 调用 foundObserver 进行对象检测
      foundObserver(value)
      // 发生变化调用dep通知订阅者
      dep.notify()
    }
  })
}

function foundObserver (value) {
  // 判断传入的数据是不是对象形式，如果是对象再新建一个 Observer 实例劫持这个对象
  if (typeof value === 'object' && value !== null) {
    return new Observer(value)
  }
}

// 实际上，一个Observer实例只能劫持一个对象，当对象内部还有对象的时候，就需要新建一个Observer进行再次劫持